import { FilterDevice, WebHidDevice } from "../types/types";
import { getMouseProductName } from "../utils";

// HID Usage Pages
const HID_USAGE_PAGES = {
  GENERIC_DESKTOP: 0x01,
  SIMULATION: 0x02,
  VR: 0x03,
  SPORT: 0x04,
  GAME: 0x05,
  KEYBOARD: 0x07,
  LED: 0x08,
  BUTTON: 0x09,
  CONSUMER: 0x0c,
  DIGITIZER: 0x0d,
  VENDOR_DEFINED_START: 0xff00,
} as const;

// Generic Desktop Page Usage IDs
const DESKTOP_USAGES = {
  POINTER: 0x01,
  MOUSE: 0x02,
  JOYSTICK: 0x04,
  GAMEPAD: 0x05,
  KEYBOARD: 0x06,
  KEYPAD: 0x07,
  MULTI_AXIS: 0x08,
} as const;

enum HIDDeviceType {
  Unknown = "unknown",
  Mouse = "mouse",
  Keyboard = "keyboard",
  Gamepad = "gamepad",
  Joystick = "joystick",
}

// 检查设备是否为特定类型
const checkDeviceType = (device: HIDDevice, type: HIDDeviceType): boolean => {
  if (!device.collections) return false;

  // 检查主要的 HID 接口
  const hasStandardInterface = device.collections.some((collection) => {
    // 检查通用桌面控制页
    if (collection.usagePage === HID_USAGE_PAGES.GENERIC_DESKTOP) {
      switch (type) {
        case HIDDeviceType.Mouse:
          return collection.usage === DESKTOP_USAGES.MOUSE;
        case HIDDeviceType.Keyboard:
          return collection.usage === DESKTOP_USAGES.KEYBOARD;
        case HIDDeviceType.Gamepad:
          return collection.usage === DESKTOP_USAGES.GAMEPAD;
        case HIDDeviceType.Joystick:
          return collection.usage === DESKTOP_USAGES.JOYSTICK;
      }
    }

    // 检查特定设备页
    switch (type) {
      case HIDDeviceType.Mouse:
        return collection.usagePage === HID_USAGE_PAGES.BUTTON;
      case HIDDeviceType.Keyboard:
        return collection.usagePage === HID_USAGE_PAGES.KEYBOARD;
    }

    return false;
  });

  // 检查厂商特定接口
  const hasVendorInterface = device.collections.some((collection) => {
    const isVendorPage =
      collection.usagePage >= HID_USAGE_PAGES.VENDOR_DEFINED_START;
    if (!isVendorPage) return false;

    // 根据已知的厂商ID和产品ID进行匹配
    switch (type) {
      case HIDDeviceType.Mouse:
        return (
          device.vendorId === 43172 || // 您的鼠标厂商ID
          device.vendorId === 43173 || // 0xa8a5 2.4G设备
          device.vendorId === 14391
        ); // 其他已知的鼠标厂商
      case HIDDeviceType.Keyboard:
        return (
          device.vendorId === 43174 || // 您的键盘厂商ID
          device.vendorId === 14391
        ); // 其他已知的键盘厂商
      default:
        return false;
    }
  });

  return hasStandardInterface || hasVendorInterface;
};

// 判断设备类型的函数
const getDeviceType = (device: HIDDevice): HIDDeviceType => {
  if (!device.collections) {
    console.log(`设备 ${device.productName} 没有 collections 属性`);
    return HIDDeviceType.Unknown;
  }

  // console.log(`\n设备名称 - ${device.productName}:`);
  // console.log(
  //   `VendorId: 0x${device.vendorId.toString(
  //     16
  //   )}, ProductId: 0x${device.productId.toString(16)}`
  // );
  // console.log(
  //   "Collections:",
  //   device.collections
  //     .map(
  //       (c) =>
  //         `\n  UsagePage: 0x${c.usagePage.toString(16)}, Usage: 0x${(
  //           c.usage || 0
  //         ).toString(16)}`
  //     )
  //     .join("")
  // );

  // 按优先级检查设备类型
  if (checkDeviceType(device, HIDDeviceType.Mouse)) {
    return HIDDeviceType.Mouse;
  }

  if (checkDeviceType(device, HIDDeviceType.Keyboard)) {
    return HIDDeviceType.Keyboard;
  }

  if (checkDeviceType(device, HIDDeviceType.Gamepad)) {
    return HIDDeviceType.Gamepad;
  }

  if (checkDeviceType(device, HIDDeviceType.Joystick)) {
    return HIDDeviceType.Joystick;
  }

  return HIDDeviceType.Unknown;
};

const globalBuffer: {
  [path: string]: { currTime: number; message: Uint8Array }[];
} = {};

const eventWaitBuffer: {
  [path: string]: ((a: Uint8Array) => void)[];
} = {};

// 发送后，等待500毫秒，如果没有数据，则不再等待
const promisify = (cb: Function) => () => {
  return Promise.race([
    new Promise((res, rej) => {
      cb((e: any, d: any) => {
        if (e) rej(e);
        else res(d);
      });
    }),
    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve([]);
      }, 500);
    }),
  ]);
};

// 根据usage和usagePage, 筛选HID设备
const filterHIDDevices = (devices: HIDDevice[]) =>
  devices.filter((device) => {
    // 添加调试日志
    // console.log(`\n检测设备: ${device.productName}`);
    // console.log(`厂商ID: 0x${device.vendorId.toString(16)}`);
    // console.log(`产品ID: 0x${device.productId.toString(16)}`);
    // console.log(
    //   "设备collections:",
    //   device.collections?.map((c) => ({
    //     usagePage: `0x${c.usagePage.toString(16)}`,
    //     usage: c.usage ? `0x${c.usage.toString(16)}` : "undefined",
    //   }))
    // );

    // 检查是否为鼠标设备
    const deviceType = getDeviceType(device);

    if (deviceType !== HIDDeviceType.Mouse) {
      // console.log(`设备类型为: ${deviceType}`);
      return false;
    }

    // 检查是否为我们支持的设备
    const isSupportedDevice = device.collections?.some(
      (collection) =>
        collection.usage === 0x0010 && collection.usagePage === 0xff01
    );

    if (!isSupportedDevice) {
      // console.log(`设备 ${device.productName} 不是受支持的设备`);
      // console.log("需要 usage: 0x0010, usagePage: 0xff01");
      return false;
    }

    return true;
  });

// 生成一个随机的地址
const getRandomAdress = () => {
  return (
    (self.crypto && self.crypto.randomUUID && self.crypto.randomUUID()) ||
    `path:${Math.random()}`
  );
};

// 添加设备信息
const addDeviceInfo = (device: HIDDevice): WebHidDevice => {
  const address = (device as any)._address || getRandomAdress();
  (device as any)._address = address;
  const HIDDevice = {
    _device: device,
    interface: 0x0001,
    vendorId: device.vendorId ?? -1,
    productId: device.productId ?? -1,
    address,
    productName: getMouseProductName(device.productName),
  };
  return (WebHid.hid_cache[address] = HIDDevice);
};

export const WebHid = {
  // HID设备缓存
  hid_cache: {} as { [address: string]: WebHidDevice },

  // 添加获取设备类型的方法
  getDeviceType: (device: HIDDevice) => getDeviceType(device),

  requestDevice: async (filters: FilterDevice[] = []) => {
    // 请求用户选择 HID 设备
    const devices = await navigator.hid.requestDevice({
      filters: filters,
    });

    // 添加设备
    devices.forEach(addDeviceInfo);
    // 从小到大排序
    const sortDevices = devices.sort((a, b) => a.vendorId - b.vendorId);
    return sortDevices[0];
  },

  getFilteredDevices: async () => {
    try {
      const hidDevices = filterHIDDevices(await navigator.hid.getDevices());
      return hidDevices;
    } catch (e) {
      return [];
    }
  },

  devices: async (requestAuthorize = false, filters: FilterDevice[] = []) => {
    let devices = await WebHid.getFilteredDevices();
    if (devices.length === 0 || requestAuthorize) {
      try {
        await WebHid.requestDevice(filters);
      } catch (error) {
        return [];
      }
      devices = await WebHid.getFilteredDevices();
    }
    return devices.map(addDeviceInfo);
  },
};

export class HidDeivce {
  _hidDevice: WebHidDevice | undefined;
  interface: number = -1;
  vendorId: number = -1;
  productId: number = -1;
  productName: string = "";
  address: string = "";
  openPromise: Promise<void> = Promise.resolve();
  constructor(address: string) {
    this._hidDevice = WebHid.hid_cache[address];

    if (this._hidDevice) {
      this.vendorId = this._hidDevice.vendorId;
      this.productId = this._hidDevice.productId;
      this.address = this._hidDevice.address;
      this.interface = this._hidDevice.interface;
      this.productName = this._hidDevice.productName;
      globalBuffer[this.address] = globalBuffer[this.address] || [];
      eventWaitBuffer[this.address] = eventWaitBuffer[this.address] || [];

      if (!this._hidDevice._device.opened) {
        this.open();
      }
    }
  }

  async open() {
    if (this._hidDevice && !this._hidDevice._device.opened) {
      this.openPromise = this._hidDevice._device.open();
      this.setupListeners();
      try {
        await this.openPromise;
      } catch (error) {
        console.log(error);
      }
    }
    return Promise.resolve();
  }

  // 添加Input事件监听
  addListeners(fn: (this: HIDDevice, ev: HIDInputReportEvent) => any) {
    if (this._hidDevice) {
      this._hidDevice._device.oninputreport = fn;
    }
  }

  // 安装Input事件监听
  setupListeners() {
    if (this._hidDevice) {
      this._hidDevice._device.addEventListener("inputreport", (e) => {
        if (eventWaitBuffer[this.address].length !== 0) {
          // It should be impossible to have a handler in the buffer
          // that has a ts that happened after the current message
          // came in
          (eventWaitBuffer[this.address].shift() as any)(
            new Uint8Array(e.data.buffer)
          );
        } else {
          globalBuffer[this.address].push({
            currTime: Date.now(),
            message: new Uint8Array(e.data.buffer),
          });
        }
      });
    }
  }

  read(fn: (err?: Error, data?: ArrayBuffer) => void) {
    eventWaitBuffer[this.address] = [];
    this.fastForwardGlobalBuffer(Date.now());
    if (globalBuffer[this.address].length > 0) {
      // this should be a noop normally
      fn(undefined, globalBuffer[this.address].shift()?.message as any);
    } else {
      eventWaitBuffer[this.address].push((data) => fn(undefined, data as any));
    }
  }

  readP = promisify((arg: any) => this.read(arg));

  fastForwardGlobalBuffer(time: number) {
    let messagesLeft = globalBuffer[this.address].length;
    while (messagesLeft) {
      messagesLeft--;
      // message in buffer happened before requested time
      if (globalBuffer[this.address][0].currTime < time) {
        globalBuffer[this.address].shift();
      } else {
        break;
      }
    }
  }

  // hid 写入数据
  async hid_write(buffer: number[]) {
    await this.openPromise;
    const data = new Uint8Array(buffer.slice(1));
    await this._hidDevice?._device.sendReport(buffer[0], data);
  }

  // hid 发送 feature数据
  async hid_send_feature_report(reportId: number, buffer: number[]) {
    await this.openPromise;
    const data = new Uint8Array(buffer.slice(1));
    await this._hidDevice?._device.sendFeatureReport(reportId, data);
    return await this._hidDevice?._device.receiveFeatureReport(reportId);
  }

  // hid 获取 feature数据
  async hid_get_feature_report(reportId: number, buffer: number[]) {
    await this.openPromise;
    const data = new Uint8Array(buffer.slice(1));
    return await this._hidDevice?._device.receiveFeatureReport(reportId);
  }
}
